home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / TurboTCP 1.0.1 / MiniTelnet.source / CTelnetTerminal.cp < prev    next >
Text File  |  1993-12-10  |  16KB  |  746 lines

  1. /*
  2. ** CTelnetTerminal.cp
  3. **
  4. **    MiniTelnet application
  5. **    Telnet terminal session document
  6. **
  7. **    Copyright © 1993, FrostByte Design / Eric Scouten
  8. **
  9. */
  10.  
  11.  
  12. #include "CTelnetTerminal.h"
  13.  
  14. #ifndef TurboTCPHeaders
  15.     #include <Packages.h>
  16.     #include <Global.h>
  17.     #include <Commands.h>
  18.     #include <TCLUtilities.h>
  19.     #include <TBUtilities.h>
  20.     #include <CApplication.h>
  21.     #include <CDesktop.h>
  22.     #include <CBartender.h>
  23.     #include <CClipboard.h>
  24.     #include <CDecorator.h>
  25.     #include <CScrollPane.h>
  26.     #include <CWindow.h>
  27. #endif
  28.  
  29. #include <CFile.h>
  30. #include "CTCPDriver.h"
  31. #include "CTCPStream.h"
  32. #include "CTCPResolverCall.h"
  33. #include "CTerminalPane.h"
  34.  
  35.  
  36. // global TCL objects
  37.  
  38. extern CApplication    *gApplication;
  39. extern CBartender    *gBartender;
  40. extern CClipboard    *gClipboard;
  41. extern CDecorator    *gDecorator;
  42. extern CDesktop    *gDesktop;
  43.  
  44.  
  45. // global TurboTCP objects
  46.  
  47. extern CTCPDriver    *gTCPDriver;
  48.  
  49.  
  50. //    —— initialization ——
  51.  
  52. /*______________________________________________________________________
  53. **
  54. ** ITelnetTerminal
  55. **
  56. **    Initialize the Telnet terminal session document.
  57. **
  58. **        aSupervisor (CApplication *):    the document’s supervisor
  59. **        printable (Boolean):            TRUE if document is printable
  60. **        recBufferSize (long):        size of the receive buffer we need
  61. **        theDefaultPort (b_16):        default IP port number
  62. **        autoReceiveSize (long):        number of entries in RDS for auto-receive,
  63. **                                    0 to disable autoreceiving
  64. **        autoReceiveNum (short):        number of auto-receive calls to issue at once
  65. **                                    must be at least 1
  66. **
  67. */
  68.  
  69. void CTelnetTerminal::ITelnetTerminal (CApplication *aSupervisor, Boolean printable,
  70.                                 long recBufferSize, b_16 theDefaultPort,
  71.                                 short autoReceiveSize, short autoReceiveNum)
  72.  
  73. {
  74.     itsTerminal = NULL;
  75.     itsTermMode = 0;
  76.     CTelnetInterpreter::ITelnetInterpreter(aSupervisor, printable, recBufferSize,
  77.                                     theDefaultPort, autoReceiveSize, autoReceiveNum);
  78.     showFileName = FALSE;
  79. }
  80.  
  81.  
  82. //    —— creating new sessions ——
  83.  
  84. /*______________________________________________________________________
  85. **
  86. ** NewSession
  87. **
  88. **    Create a new window for document. Pulls information from the settings record which
  89. **    it receives and opens a session accordingly.
  90. **
  91. **        theSettings (TelnetSettingsRec *):    the settings record to use
  92. **
  93. */
  94.  
  95. void CTelnetTerminal::NewSession (TelnetSettingsRec *theSettings)
  96.  
  97. {
  98.     // copy the settings record
  99.     
  100.     BlockMove(theSettings, &r, sizeof (TelnetSettingsRec));
  101.     BlockMove(&r.hostName, &hostCName, 256);
  102.     goAwayOnClose = r.closeOnSessionEnd;
  103.     showDebug = r.showDebug;
  104.     
  105.     // build a window immediately
  106.     
  107.     BuildWindow();
  108.     
  109.     // open host by name
  110.     
  111.     #if _TestTerminal
  112.         itsTerminal->DoWriteStr("No TCP session. Just type onto terminal.\r\n");
  113.     #else
  114.         itsStream->SetULPTimeoutValue(20);
  115.         OpenUserHost((char *) &r.hostName, defaultPort, TRUE);
  116.     #endif
  117.  
  118. }
  119.  
  120.  
  121. /*______________________________________________________________________
  122. **
  123. ** BuildWindow
  124. **
  125. **    Build a window for a terminal connection.
  126. **
  127. */
  128.  
  129. void CTelnetTerminal::BuildWindow (void)
  130.  
  131. {
  132.     CScrollPane    *theScrollPane;
  133.     CTerminalPane    *theMainPane;
  134.     Rect            sizeRect;
  135.     
  136.     // create a window
  137.  
  138.     itsWindow = new(CWindow);
  139.     itsWindow->IWindow(WINDTelnet, FALSE, gDesktop, this);
  140.  
  141.     // set maximum size of window
  142.  
  143.     SetRect(&sizeRect, 100, 100, sizeX+16, sizeY+16);
  144.     itsWindow->SetSizeRect(&sizeRect);
  145.  
  146.     // create the scrolling pane
  147.  
  148.     theScrollPane = new(CScrollPane);
  149.     theScrollPane->IScrollPane(itsWindow, this, 10, 10, 0, 0,
  150.                             sizELASTIC, sizELASTIC,
  151.                             TRUE, TRUE, TRUE);
  152.     theScrollPane->FitToEnclFrame(TRUE, TRUE);
  153.  
  154.     // create the main view pane
  155.  
  156.     theMainPane = new(CTerminalPane);
  157.     theMainPane->ITerminalPane(theScrollPane, this, 0, 0, 0, 0,
  158.                                  sizELASTIC, sizELASTIC);
  159.     itsMainPane = theMainPane;
  160.     itsTerminal = theMainPane;
  161.     itsGopher = theMainPane;
  162.     theMainPane->FitToEnclosure(TRUE, TRUE);
  163.  
  164.     // make sure the scroll pane knows about the panorama
  165.  
  166.     theScrollPane->InstallPanorama(theMainPane);
  167.     
  168.     // place, size, and title the window
  169.  
  170.     gDecorator->PlaceNewWindow(itsWindow);
  171.     AutoTitle();
  172.     itsWindow->Select();
  173.  
  174. }
  175.  
  176.  
  177. /*______________________________________________________________________
  178. **
  179. ** AutoTitle
  180. **
  181. **    Sets title for window. Turns on or off cursor blinking depending on whether session
  182. **    is established.
  183. **
  184. */
  185.  
  186. void CTelnetTerminal::AutoTitle (void)
  187.  
  188. {
  189.     CTCPSessionDoc::AutoTitle();
  190.     itsTerminal->SetBlinking(sessionReady);
  191. }
  192.  
  193.  
  194. //    —— command/event handling ——
  195.  
  196. /*______________________________________________________________________
  197. **
  198. ** DoCommand
  199. **
  200. **    Handle all commands document can understand.
  201. **
  202. **        theCommand (long):    command number that was issued
  203. **
  204. */
  205.  
  206. void CTelnetTerminal::DoCommand (long theCommand)
  207.  
  208. {
  209.     char addrStr [18];                        // used for send IP addr command
  210.     
  211.     switch (theCommand) {
  212.     
  213.         // Edit menu: support pasting TEXT
  214.         
  215.         case cmdPaste:
  216.             DoPaste();
  217.             break;
  218.         
  219.         // Telnet menu: send various strings to server
  220.         
  221.         case cmdSendSynch:
  222.             if (showDebug)
  223.                 PrintDebugStr("{Urgent IAC DM}");
  224.             itsStream->SetNextUrgent(FALSE);
  225.             itsStream->SendCString("\377\362");
  226.             break;
  227.             
  228.         case cmdSendBreak:
  229.             if (showDebug)
  230.                 PrintDebugStr("{Urgent IAC BRK IAC DM}");
  231.             itsStream->SetNextUrgent(FALSE);
  232.             itsStream->SendCString("\377\363\377\362");
  233.             break;
  234.             
  235.         case cmdSendAO:
  236.             if (showDebug)
  237.                 PrintDebugStr("{Urgent IAC AO IAC DM}");
  238.             itsStream->SetNextUrgent(FALSE);
  239.             itsStream->SendCString("\377\365\377\362");
  240.             break;
  241.             
  242.         case cmdSendIP:
  243.             if (showDebug)
  244.                 PrintDebugStr("{Urgent IAC IP IAC DM}");
  245.             itsStream->SetNextUrgent(FALSE);
  246.             itsStream->SendCString("\377\364\377\362");
  247.             break;
  248.             
  249.         case cmdSendAYT:
  250.             if (showDebug)
  251.                 PrintDebugStr("{Urgent IAC AYT IAC DM}");
  252.             itsStream->SetNextUrgent(FALSE);
  253.             itsStream->SendCString("\377\366\377\362");
  254.             break;
  255.             
  256.         case cmdSendGA:
  257.             if (showDebug)
  258.                 PrintDebugStr("{IAC GA}");
  259.             itsStream->SendCString("\377\371");
  260.             break;
  261.             
  262.         case cmdSendEC:
  263.             if (showDebug)
  264.                 PrintDebugStr("{IAC EC}");
  265.             itsStream->SendCString("\377\367");
  266.             break;
  267.             
  268.         case cmdSendEL:
  269.             if (showDebug)
  270.                 PrintDebugStr("{IAC EL}");
  271.             itsStream->SendCString("\377\370");
  272.             break;
  273.             
  274.         case cmdSendIPAddr:
  275.             itsResolver->DoAddrToStr(gTCPDriver->GetIPAddr(),
  276.                         (char *) &addrStr);
  277.             itsStream->SendCString((char *) &addrStr);
  278.             break;
  279.  
  280.         case cmdShowDebug:
  281.             showDebug = !showDebug;
  282.             r.showDebug = showDebug;
  283.             break;
  284.             
  285.         // not ours, send along the chain
  286.         
  287.         default:
  288.             inherited::DoCommand(theCommand);
  289.     }
  290. }
  291.  
  292.  
  293. /*______________________________________________________________________
  294. **
  295. ** DoKeyDown
  296. **
  297. **    Process key-down events. Parses CR, LF codes and generates the
  298. **    standard CR/LF sequence when CR is passed and ignores LF.
  299. **
  300. **        theChar (char):                the character that was entered
  301. **        keyCode (byte):            the Mac ADB key number for the key that was pressed
  302. **        macEvent (EventRecord *):    the entire event record
  303. **
  304. */
  305.  
  306. void CTelnetTerminal::DoKeyDown (char theChar, Byte keyCode, EventRecord *macEvent)
  307.  
  308. {
  309.  
  310.     // make sure this isn’t an errant command key
  311.     
  312.     if (!((*macEvent).modifiers & cmdKey)) {
  313.         if (theChar != charLF) {
  314.             #if _TestTerminal
  315.                 HandleNVTChar(theChar);
  316.                 if (theChar == charCR)
  317.                     HandleNVTChar(charLF);
  318.             #else
  319.                 if (!sessionReady) {
  320.                     SysBeep(0);
  321.                     return;
  322.                 }
  323.         
  324.                 if (theChar == charBS)
  325.                     itsStream->SendChar(r.backspaceChar);
  326.                 else
  327.                     itsStream->SendChar(theChar);
  328.         
  329.                 if (theChar == charCR)
  330.                     itsStream->SendChar(charLF);
  331.             #endif
  332.         itsTerminal->ScrollToSelection();
  333.         }
  334.     }
  335.  
  336. }
  337.  
  338.  
  339. /*______________________________________________________________________
  340. **
  341. ** DoAutoKey
  342. **
  343. **    Process automatic (repeated) key-down events.
  344. **
  345. **        theChar (char):                the character that was entered
  346. **        keyCode (byte):            the Mac ADB key number for the key that was pressed
  347. **        macEvent (EventRecord *):    the entire event record
  348. **
  349. */
  350.  
  351. void CTelnetTerminal::DoAutoKey (char theChar, Byte keyCode,  EventRecord *macEvent)
  352.  
  353. {
  354.     DoKeyDown(theChar, keyCode, macEvent);    // boy, wasn’t that simple!
  355. }
  356.  
  357.  
  358. /*______________________________________________________________________
  359. **
  360. ** DoPaste
  361. **
  362. **    Respond to Paste command by sending all characters from the
  363. **    clipboard to the remote host.
  364. **
  365. */
  366.  
  367. void CTelnetTerminal::DoPaste (void)
  368.  
  369. {
  370.     Handle    theData;
  371.     char        lineBfr[83];
  372.     char        *p, *pl, *lb;
  373.     long        rem;
  374.     short    i;
  375.     
  376.     // get the clipboard contents
  377.     
  378.     if (!(gClipboard->GetData('TEXT', &theData)))
  379.         return;
  380.     HLock(theData);
  381.     
  382.     // send it in line or 80-char chunks
  383.     
  384.     p = *theData;
  385.     rem = GetHandleSize(theData);
  386.     
  387.     while (rem > 0) {
  388.     
  389.         // fetch a line (or 81 chars)
  390.         
  391.         i = 0;
  392.         pl = p;
  393.         lb = &lineBfr[0];
  394.         while ((rem-(i) > 0) && (*pl != charCR) && (i < 80))
  395.             i++, *lb++ = *pl++;
  396.         if (*pl == charCR) {
  397.             i++, *pl++;
  398.             *lb++ = charCR;
  399.             *lb++ = charLF;
  400.         }
  401.         *lb = '\0';
  402.     
  403.         // send the line
  404.         
  405.         #if _TestTerminal
  406.             HandleNVTLine((char *) &lineBfr);
  407.         #else
  408.             itsStream->SendBfrCpy((char *) &lineBfr, cstrlen(lineBfr));
  409.         #endif
  410.     
  411.         rem -= i;
  412.         p += i;
  413.         
  414.     }
  415.     
  416.     HUnlock(theData);
  417.     DisposHandle(theData);
  418. }
  419.  
  420.  
  421. /*______________________________________________________________________
  422. **
  423. ** UpdateMenus
  424. **
  425. **    Enable Telnet-specific commands.
  426. **
  427. */
  428.  
  429. void CTelnetTerminal::UpdateMenus (void)
  430.  
  431. {
  432.     CDocument::UpdateMenus();
  433.     
  434.     // enable “Paste” command only if valid Clipboard
  435.     
  436.     #if _TestTerminal
  437.         if (gClipboard->DataSize('TEXT'))
  438.             gBartender->EnableCmd(cmdPaste);
  439.     #else
  440.         if (sessionReady && (gClipboard->DataSize('TEXT')))
  441.             gBartender->EnableCmd(cmdPaste);
  442.     #endif
  443.  
  444.     
  445.     // enable “Send…” commands only if session is active
  446.  
  447.     if (sessionReady) {
  448.         gBartender->EnableCmd(cmdSendSynch);
  449.         gBartender->EnableCmd(cmdSendBreak);
  450.         gBartender->EnableCmd(cmdSendAO);
  451.         gBartender->EnableCmd(cmdSendIP);
  452.         gBartender->EnableCmd(cmdSendAYT);
  453.         gBartender->EnableCmd(cmdSendGA);
  454.         gBartender->EnableCmd(cmdSendEC);
  455.         gBartender->EnableCmd(cmdSendEL);
  456.         gBartender->EnableCmd(cmdSendIPAddr);
  457.         gBartender->EnableCmd(cmdShowDebug);
  458.         gBartender->CheckMarkCmd(cmdShowDebug, showDebug);
  459.     }
  460.  
  461. }
  462.  
  463.  
  464. //    —— data handling methods ——
  465.  
  466. /*______________________________________________________________________
  467. **
  468. ** HandleNVTChar
  469. **
  470. **    Process a character which was recieved over Telnet for the network virtual terminal.
  471. **
  472. **        theChar (char):    the character which was received
  473. **
  474. */
  475.  
  476. void CTelnetTerminal::HandleNVTChar (char theChar)
  477.  
  478. {
  479.     itsTerminal->DoWriteChar(theChar);
  480. }
  481.  
  482.  
  483. /*______________________________________________________________________
  484. **
  485. ** HandleNVTLine
  486. **
  487. **    Process a line (or <81 chars) which was recieved over Telnet for the network virtual
  488. **    terminal.
  489. **
  490. **        theLine (char *):    the line which was received (C string)
  491. **
  492. */
  493.  
  494. void CTelnetTerminal::HandleNVTLine (char *theLine)
  495.  
  496. {
  497.     itsTerminal->DoWriteStr(theLine);
  498. }
  499.  
  500.  
  501. //    —— Telnet command handling ——
  502.  
  503.  
  504. /*______________________________________________________________________
  505. **
  506. ** ReceivedDo
  507. **
  508. **    Respond to a Telnet [IAC DO option] sequence.
  509. **
  510. **        theOption (uChar):    the option code
  511. **
  512. */
  513.  
  514. void CTelnetTerminal::ReceivedDo (uchar theOption)
  515.  
  516. {
  517.     uchar respondStr[4];
  518.     
  519.     // parse the option and respond to it
  520.     
  521.     switch (theOption) {
  522.     
  523.         // accept terminal type option only
  524.         
  525.         case optTERMINAL_TYPE:
  526.             respondStr[0] = charIAC;
  527.             respondStr[1] = escWILL;
  528.             respondStr[2] = theOption;
  529.             respondStr[3] = '\0';
  530.             if (showDebug) {
  531.                 PrintDebugStr("{IAC WILL");
  532.                 PrintDebugCharNum(theOption, ' ', '}');
  533.             }
  534.             itsStream->SendCString((char *) &respondStr);
  535.             break;
  536.         
  537.         // reject the remaining options
  538.         
  539.         default:
  540.             CTelnetInterpreter::ReceivedDo(theOption);
  541.     }
  542.  
  543. }
  544.  
  545.  
  546. /*______________________________________________________________________
  547. **
  548. ** ReceivedAYT
  549. **
  550. **    Respond to a Telnet [IAC AYT] sequence.
  551. **
  552. */
  553.  
  554. void CTelnetTerminal::ReceivedAYT (void)
  555.  
  556. {
  557.     if (showDebug)
  558.         PrintDebugStr("{Yes}");
  559.     itsStream->SendCString("[Yes]");
  560. }
  561.  
  562.  
  563. /*______________________________________________________________________
  564. **
  565. ** ReceivedEC
  566. **
  567. **    Respond to a Telnet [IAC EC] sequence.
  568. **
  569. */
  570.  
  571. void CTelnetTerminal::ReceivedEC (void)
  572.  
  573. {
  574.     itsTerminal->DoEraseChar();
  575. }
  576.  
  577.  
  578. /*______________________________________________________________________
  579. **
  580. ** ReceivedEL
  581. **
  582. **    Respond to a Telnet [IAC EL] sequence.
  583. **
  584. */
  585.  
  586. void CTelnetTerminal::ReceivedEL (void)
  587.  
  588. {
  589.     itsTerminal->DoEraseLine();
  590. }
  591.  
  592.  
  593. /*______________________________________________________________________
  594. **
  595. ** ReceivedSE
  596. **
  597. **    Parse subnegotiation parameters. Called when [IAC SE] is received.
  598. **
  599. */
  600.  
  601. void CTelnetTerminal::ReceivedSE (void)
  602.  
  603. {
  604.     // dispatch to an option-parsing routine
  605.     
  606.     if (sbBfrIndex < 1)
  607.         return;
  608.     switch (sbBfr[0]) {
  609.  
  610.         case optTERMINAL_TYPE:
  611.             OptionTerminalType();
  612.             break;
  613.         
  614.         default:
  615.             CTelnetInterpreter::ReceivedSE();
  616.     }
  617.     
  618. }
  619.  
  620.  
  621. //    —— Telnet option handling ——
  622.  
  623. /*______________________________________________________________________
  624. **
  625. ** OptionTerminalType
  626. **
  627. **    Interpret an [IAC SB TERMINAL-TYPE ... IAC SE] sequence. Default method always says
  628. **    this is an unknown terminal.
  629. **
  630. */
  631.  
  632. void CTelnetTerminal::OptionTerminalType (void)
  633.  
  634. {
  635.     uchar    respondStr [47];
  636.     short    i = 2;
  637.     
  638.     // respond only to TERMINAL-TYPE SEND queries; ignore others
  639.     
  640.     if (sbBfr[1] == 1) {
  641.  
  642.         // build a response string
  643.         
  644.         respondStr[0] = charIAC;
  645.         respondStr[1] = escSB;
  646.         respondStr[2] = optTERMINAL_TYPE;
  647.         
  648.         // switch to next terminal emulation
  649.         
  650.         itsTermMode++;
  651.         if (itsTermMode > termMax)
  652.             itsTermMode = 0;
  653.         // SetTerminalMode(itsTermMode);
  654.         
  655.         // get name of terminal emulation
  656.         
  657.         GetTerminalName(itsTermMode, &respondStr[3]);
  658.         
  659.         // display response if debugging mode enabled
  660.                 
  661.         if (showDebug) {
  662.             PrintDebugStr("{IAC SB TERM-TYPE IS ");
  663.             PrintDebugStr((char *) &respondStr[3]);
  664.             PrintDebugStr(" IAC SE}");
  665.         }
  666.         
  667.         // add end of SB string & send it
  668.         
  669.         while (respondStr[++i])
  670.             ;
  671.         respondStr[i] = charIAC;
  672.         respondStr[i+1] = escSE;
  673.         respondStr[i+2] = '\0';    
  674.         itsStream->SendCString((char *) &respondStr);
  675.     }
  676.     
  677. }
  678.  
  679.  
  680. //    —— terminal emulation handling ——
  681.  
  682. /*______________________________________________________________________
  683. **
  684. ** GetTerminalName
  685. **
  686. **    Return the Internet assigned name for the terminal being used.
  687. **
  688. **        termIndex (short):    the terminal emulation number
  689. **        termStr (uchar *):    buffer to receive the terminal name (max 40 chars)
  690. **
  691. */
  692.  
  693. void CTelnetTerminal::GetTerminalName (short termIndex, uchar *termStr)
  694.  
  695. {
  696.     uchar theTerm[41] = "UNKNOWN";        // for now, all we have is “UNKNOWN”
  697.     BlockMove(&theTerm, termStr, 41);
  698. }
  699.  
  700.  
  701. /*______________________________________________________________________
  702. **
  703. **    —— debugging methods ——
  704. **
  705. **    These methods were useful to me in debugging the implementation of various Telnet
  706. **    options. They are enabled by the “Show debugging codes” option of the settings dialog.
  707. **
  708. */
  709.  
  710. /*______________________________________________________________________
  711. **
  712. ** PrintDebugStr
  713. **
  714. **    Write a string to the terminal for debugging purposes.
  715. **
  716. **        theDebugStr (char *):    the string to write
  717. **
  718. */
  719.  
  720. void CTelnetTerminal::PrintDebugStr (char *theDebugStr)
  721.  
  722. {
  723.     itsTerminal->DoWriteStr(theDebugStr);
  724. }
  725.  
  726.  
  727. /*______________________________________________________________________
  728. **
  729. ** PrintDebugCharNum
  730. **
  731. **    Print a character number for debugging. Provided as a debugging routine. The number
  732. **    is bracketed by the two characters indicated, i.e. if you call PrintDebugCharNum('!', '[', ']'),
  733. **    you get [33] written to the terminal.
  734. **
  735. **        theChar (char):        the character number to write
  736. **        leftBracket (char):    prefix to character number
  737. **        rightBracket (char):    suffix to character number
  738. **
  739. */
  740.  
  741. void CTelnetTerminal::PrintDebugCharNum (char theChar, char leftBracket, char rightBracket)
  742.  
  743. {
  744.     itsTerminal->DoWriteCharNum(theChar, leftBracket, rightBracket);
  745. }
  746.